package com.vzaar.uploader
{
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.HTTPStatusEvent;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.FileReference;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import flash.system.Security;
import flash.xml.XMLDocument;
import flash.xml.XMLNode;

public class S3Uploader extends EventDispatcher
	{
		[Event(name="open", type="flash.events.Event.OPEN")]
		[Event(name="uploadCompleteData", type="flash.events.DataEvent.UPLOAD_COMPLETE_DATA")]        
		[Event(name="ioError", type="flash.events.IOErrorEvent.IO_ERROR")]
		[Event(name="securityError", type="flash.events.SecurityErrorEvent.SECURITY_ERROR")]
		[Event(name="progress", type="flash.events.ProgressEvent.PROGRESS")]
		
		private static const ENDPOINT:String = ".s3.amazonaws.com";
		private var URL:String ;
		private var fileRef:FileReference;
		private var options:S3UploadOptions;
		
		private var accessKeyId:String;
		private var key:String;
		private var bucket:String;
		private var expDate:String;
		public function S3Uploader(accessKeyID:String,key:String,bucket:String,expDate:String,uploadOption:S3UploadOptions = null)
		{
			this.accessKeyId = accessKeyID;
			this.key = key;
			this.bucket = bucket;
			this.expDate = expDate;
			this.options = uploadOption;
			
			URL = "http://" + bucket + ENDPOINT;					
		}
	
		private function buildVariable(file:FileReference):URLVariables
		{
			var data:URLVariables = new URLVariables();						
			
			data.key = key;
			data.acl = options.acl;			
			data.AWSAccessKeyId  = accessKeyId;	
			data.policy = options.policy;
			data.signature = options.signature;
			data["Content-Type"] = options.contentType;
			data.success_action_status = "201";
			data.filename = file.name;
			
			return data;
		}
				
		public function upload(file:FileReference):void
		{
			var urlReq:URLRequest = new URLRequest(URL);
			//urlReq.contentType = "multipart/form-data";
			urlReq.method = URLRequestMethod.POST;
			urlReq.data = buildVariable(file);	
			
			var headerUserAgent:URLRequestHeader = new URLRequestHeader('User-Agent' , 'Vzaar API Client');
			var headerACL:URLRequestHeader = new URLRequestHeader('x-amz-acl' , options.acl);
			var headerEnclosure:URLRequestHeader = new URLRequestHeader('Enclosure-Type' , 'multipart/form-data');
			
			urlReq.requestHeaders.push(headerUserAgent);
			urlReq.requestHeaders.push(headerEnclosure);
			urlReq.requestHeaders.push(headerACL);
			
			Security.loadPolicyFile(URL + "/crossdomain.xml");
			// set up event handlers *****************************************************
			file.addEventListener(Event.OPEN, onOpen);
			file.addEventListener(ProgressEvent.PROGRESS, onProgress);
			file.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
			file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadCompleteData);
			file.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHttpStatus);
			// *****************************************************************************
			
			file.upload(urlReq, "file" , false);
		}				
		
		private function onOpen(event:Event):void {
			dispatchEvent(event);
		}
		private function onIOError(event:IOErrorEvent):void {
			/*
			* FileReference.upload likes to send cryptic IOErrors when it doesn't get a status code that it likes.
			* If we already got an error HTTP status code, don't propagate this event since the HTTPStatusEvent
			* event handler dispatches an IOErrorEvent.
			*/			
			this.dispatchEvent(event);			
		}
		private function onSecurityError(event:SecurityErrorEvent):void {
			this.dispatchEvent(event);
		}
		private function onProgress(event:ProgressEvent):void {
			this.dispatchEvent(event);
		}
		private function onUploadCompleteData(event:DataEvent):void {
			var data:String = event.data;			
			if(isError(data)) {
				this.dispatchEvent(
					new IOErrorEvent(IOErrorEvent.IO_ERROR, event.bubbles, event.cancelable, "Amazon S3 returned an error: " + data + ".")
				);
			} else {
				this.dispatchEvent(new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, data));
			}
		}
		
		public function isError(responseText:String):Boolean {
			var xml:XMLDocument = new XMLDocument();
			xml.ignoreWhite = true;
			xml.parseXML(responseText);
			var root:XMLNode = xml.firstChild;
			if( root == null || root.nodeName != "Error" )
			{
				return false;
			}
			return true;
		}
		
		private function onHttpStatus(event:HTTPStatusEvent):void 
		{						
			if(Math.floor(event.status/100) == 2) {
				this.dispatchEvent(new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, "Amazon S3 returned HTTP status " + event.status.toString() + "."));
			} else {
				this.dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR, event.bubbles, event.cancelable, "Amazon S3 returned an error: HTTP status " + event.status.toString() + "."));
			}
		}
	}
}